import {reactive} from "vue";
import {forEachValue} from "@/vuex/utils";
import {storeKey} from "@/vuex/injectKey";

export class Store {
  constructor(options) {
    const store = this;
    store._state = reactive({data: options.state});
    //                                ↑ 之所以包一层, 是因为我们vuex里有一个api replaceState, 在里面 store._state.data = xxx 这样修改数据的话 就能响应式, 如果不包,  store._state = xxx, 这样就是完全换成一个新的对象, 是不会触发任何代理动作的 更别提响应式了

    const _getters = options.getters;
    store.getters = {};
    forEachValue(_getters, function (getterFn, key) {
      Object.defineProperty(store.getters, key, {
        get: () => getterFn(store.state), //现在可能会有点性能问题 因为每次都会重新执行, 但我们这里又不能用computed实现, 因为组件销毁了 会移除对应组件的计算属性 (vue3.0~3.1) 预计3.2会盖掉这个bug
        enumerable: true
      });
    });

    store._mutations = Object.create(null);
    store._actions = Object.create(null);
    const _mutations = options.mutations;
    const _actions = options.actions;
    forEachValue(_mutations, (mutation, key) => {
      store._mutations[key] = (payload) => {
        mutation.call(store, store.state, payload);
      };
    });
    forEachValue(_actions, (action, key) => {
      store._actions[key] = (payload) => {
        action.call(store, store, payload);
      };
    });
  }

  // es7 会编译成 bind 方法
  // 因为我们一般会 const {dispatch} = useStore() 这样解构出来使用
  commit = (type,payload) => {
    this._mutations[type](payload);
  };

  dispatch = (type,payload) => {
    this._actions[type](payload);
  };

  get state(){
    return this._state.data;
  }

  install(app, injectKey) {
    app.provide(injectKey || storeKey, this);

    //↓等价于: Vue.prototype.$store = this
    app.config.globalProperties.$store = this;
  }
}
